<?php

declare(strict_types=1);

namespace So\Aspect;

use Hyperf\Contract\ConfigInterface;
use Hyperf\Di\Annotation\Inject;
use So\Annotation\Validation as ValidationAnnotation;
use Hyperf\Di\Annotation\Aspect;
use Hyperf\Di\Aop\AbstractAspect;
use Hyperf\Di\Aop\ProceedingJoinPoint;
use So\Request;
use So\Response;
use Hyperf\Validation\Contract\ValidatorFactoryInterface;

/**
 * 切入Validation注解实现验证器
 * @Aspect
 * @author Saopig <1306222220@qq.com>
 */
class Validation extends AbstractAspect
{

    /**
     * @Inject()
     * @var Response
     */
    protected $response;

    /**
     * @Inject()
     * @var ConfigInterface
     */
    protected $config;

    /**
     * @Inject()
     * @var Request
     */
    protected $request;

    /**
     * @Inject()
     * @var ValidatorFactoryInterface
     */
    protected $validationFactory;

    /**
     * @var string[]
     */
    public $annotations = [
        ValidationAnnotation::class,
    ];

    public function process(ProceedingJoinPoint $proceedingJoinPoint)
    {
        $classname = array_filter(explode("\\", $proceedingJoinPoint->className));
        $names = ['Controller', 'Service', 'Logic'];
        foreach ($classname as $k => &$v) {
            if (($k + 1) !== count($classname)) {
                in_array($v, $names) && $v = 'Request';
            }else{
                $v = str_replace($names, 'Request', $v);
            }
        }
        $validation = implode("\\",$classname);
        $Annotation = $proceedingJoinPoint->getAnnotationMetadata();
        $ValidationAnnotation = $Annotation->method[ValidationAnnotation::class] ?? [];
        $scene = $ValidationAnnotation->scene ?: '';
        $security = $ValidationAnnotation->security ?: false;
        $var = $ValidationAnnotation->var;
        $filter = $ValidationAnnotation->filter;
        $get = $security ? 'all' : 'input';
        $data = $this->request->$get();
        $exception = $this->config->get('so.exception.validation');
        if (!class_exists($validation)) {
            if ($exception) {
                throw new $exception('Validation is not defined');
            }
            return $this->response->error('Validation is not defined');
        }
        $validation = make($validation);
        $validator = $this->validationFactory->make($data, $validation->getSceneRules($scene), $validation->messages());
        if ($validator->fails()) {
            $errorMessage = $validator->errors()->first();
            if ($exception) {
                throw new $exception($errorMessage);
            }
            return $this->response->error($errorMessage);
        }
        $filter && $data = [];
        $proceedingJoinPoint->arguments['keys'][$var] = array_merge($data,$validator->validated());
        return $proceedingJoinPoint->process();
    }

}